﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;

namespace Galaktika.BI.Silverlight.ClientApp.Web.Runtime.Services
{
    using Galaktika.BI.Silverlight.ClientApp.Web.DAL;
    using Galaktika.BI.Extensibility.Dom;
    using System.Text;

    public class ExecutionContext : IExecutionContext
    {
        public ExecutionContext()
        {
        }

        #region IExecutionContext Members

        int m_SuspendRequest = 0;
        public void SuspendRefresh()
        {
            m_SuspendRequest++;
        }

        public void ResumeRefresh()
        {
            m_SuspendRequest--;
        }

        public void DoDrillDown(IBindingContext context)
        {
            //DataBinder.RefreshImmediately(context, null);
        }

        private const int MAX_NESTED_VAR_DEPTH = 20;
        public string Parse(IBindingContext context, string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return string.Empty;
            }
            string pat = @"{{([^{}]+)}}";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            for (int i = 0; i < MAX_NESTED_VAR_DEPTH; i++)
            {
                // Match the regular expression pattern against a text string.
                Match m = r.Match(input);
                if (!m.Success)
                {
                    break;
                }
                StringBuilder sb = new StringBuilder();
                int startIndex = 0;
                bool isJustUnconvertableCaptures = true;
                while (m.Success)
                {
                    Group group = m.Groups[1];
                    Capture capture = m.Captures[0];
                    sb.Append(input.Substring(startIndex, capture.Index - startIndex));

                    string varName = group.Value;
                    BrickVariable var = this.GetVariable(context, varName, true);
                    string varValue = string.Empty;
                    if (var != null)
                    {
                        varValue = var.CurrentValue;
                        isJustUnconvertableCaptures = false;
                    }
                    else
                    {
                        // check function invoke
                        FuncInvokeCheckResult check = ScriptEngine.CheckFuncInvoke(varName);
                        string invokeRes = null;
                        if (check.IsSuccess)
                        {
                            invokeRes = ScriptEngine.InvokeFunction(context, check, ServerResources.ServerAssembly);
                        }
                        if (invokeRes != null)
                        {
                            varValue = invokeRes;
                            isJustUnconvertableCaptures = false;
                        }
                        else
                        {
                            varValue = capture.Value;//string.Format("???{0}???", varName);
                        }
                    }
                    //input = input.Substring(0, m.Index) + varValue + input.Substring(m.Index + m.Length);
                    //m = r.Match(input);
                    sb.Append(varValue);
                    startIndex = capture.Index + capture.Length;
                    m = m.NextMatch();
                }
                sb.Append(input.Substring(startIndex, input.Length - startIndex));
                input = sb.ToString();

                if (isJustUnconvertableCaptures)
                {
                    break;
                }
            }
            return input;
        }

        public BrickVariable GetVariable(IBindingContext context, string name)
        {
            return this.GetVariable(context, name, false);
        }

        public BrickVariable GetVariable(IBindingContext context, string name, bool createIfNotExist)
        {
            DataBinder binder = DataBinder.Current;
            if (context == null)
            {
                context = binder.DefaultContext;
            }
            VariablesContainer storage = binder.GetVariables(context);
            VariablesContainer  defStorage = binder.DefaultVariables;
            //if (storage == null)
            //{
            //    if (createIfNotExist)
            //    {
            //        storage = new SafeDictionary<string, BrickVariable>();
            //        DataBinder.Variables.Add(context, storage);
            //    }
            //    else
            //    {
            //        storage = defStorage;
            //    }
            //}

            DalVariable var = storage[name];
            DalVariable defVar = defStorage[name];
            if (var == null)
            {
                if (defVar != null)
                {
                    if (createIfNotExist)
                    {
                        var = defVar.Clone();
                        storage.Add(var.Name, var);
                    }
                    else
                    {
                        var = defVar;
                    }
                }
            }

            if (var != null)
                return var.ToBrick();

            return null;
        }

        public IDictionary<string, string> GetStateVariables(IBindingContext context)
        {
            DataBinder binder = DataBinder.Current;

            VariablesContainer vars = binder.GetVariables(context);
            SafeDictionary<string, string> res = new SafeDictionary<string, string>();
            if (vars != null)
            {
                foreach (KeyValuePair<string, DalVariable> kv in vars)
                {
                    res.Add(kv.Key, kv.Value.CurrentValue);
                }

                VariablesContainer allVars = binder.DefaultVariables;
                foreach (KeyValuePair<string, DalVariable> defVar in allVars)
                {
                    if (!res.ContainsKey(defVar.Key))
                    {
                        res.Add(defVar.Key, defVar.Value.CurrentValue);
                    }
                }
            }
            return res;
        }

        public bool ModifyVariable(IBindingContext context, string name, string newValue)
        {
            return this.ModifyVariable(context, name, newValue, m_SuspendRequest == 0);
        }

        private bool ModifyVariable(IBindingContext context, string name, string newValue, bool applyInstantly)
        {
            BrickVariable var = this.GetVariable(context, name, true);
            if (var == null)
            {
                return false;
            }

            if (var.CurrentValue == newValue)
            {
                return false;
            }

            // save previos value
            // if new value is invalid - restore previos
            string oldValue = var.CurrentValue;
            var.CurrentValue = newValue;
            bool res = true;
            if (applyInstantly)
            {
                if (!this.RefreshContext(context))
                {
                    var.CurrentValue = oldValue;
                    res = false;
                }
            }

            return res;
        }

        private bool RefreshContext(IBindingContext context)
        {
            bool res = true;
            try
            {
                context.IsDirty = true;
                context.Reset();
                DataBinder.Current.Refresh(context);
            }
            catch //(Exception exc)
            {
                //IOutputService log = (IOutputService)context.Services.GetService(typeof(IOutputService));
                //if (log != null)
                //{
                //    log.WriteException(exc, true);
                //}
                //res = false;
            }

            return res;
        }

        public void ModifyVariables(IBindingContext context, IDictionary<string, string> newValues)
        {
            foreach (KeyValuePair<string, string> kv in newValues)
            {
                this.ModifyVariable(context, kv.Key, kv.Value, false);
            }
            if (newValues.Count > 0)
            {
                this.RefreshContext(context);
            }
        }

        public BrickObject GetNamedObject(IBindingContext context, string name)
        {
            return DataBinder.Current.GetNamedObject(name);
        }

        #endregion    
    }
}
